<?php
/**
 * This file is part of DomAr.
 * 
 * DomAr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * DomAr is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with DomAr.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @link http://inditel.ee/
 * @copyright 2007-2009 Inditel Meedia OÜ
 * @author Oliver Leisalu <oliver@inditel.ee>
 */
 
/**
* Plugin that helps to control dates.
*
* @package DomAr
* @subpackage Plugin
*/
class DomArDate extends DomArPlugin {

	/**
	 * Current date in format Y-m-d
	 * @var String
	 */
	protected $date = NULL;
	/**
	 * Current date in format Ymd
	 * @var Int
	 */
	protected $dateNum = NULL;
	
	/**
	 * @param String $date
	 */
	public function __construct( $date = NULL ) {
		$this->setDate( $date );
	}
	
	/**
	 * @param String $date
	 * @return Int
	 */
	public function validate( $date ) {
		if( $date == 0 or $date === NULL )
			return true;
	
		$parse = self::parseDate( $date );
		
		if( $parse === false ) 
			return false;
		
		if( (int)$parse['month'] == 0 && (int)$parse['day'] == 0 && (int)$parse['year'] == 0)
			return true;
		
		if( (int)$parse['month'] == 0 && (int)$parse['day'] == 0 && (int)$parse['year'] != 0)
			return true;
			
		return checkdate( $parse['month'], $parse['day'], $parse['year']);
	}
	
	// -----------------------------------------------------
	// GET
	// -----------------------------------------------------
	
	/**
	 * @return Bool
	 */
	public function isNull() {
		return $this->date == NULL;
	}
	/**
	 * @return Bool
	 */
	public function inPast() {
		return $this->dateNum < (int)date('Ymd');
	}
	/**
	 * @return Bool
	 */
	public function inFuture() {
		return !$this->inPast();
	}
	/**
	 * @return Bool
	 */
	public function isToday() {
		return $this->dateNum == (int)date('Ymd');
	}
	/**
	 * @return String
	 */
	public function date( $format = DOMAR_DATE_DEFAULT_FORMAT ) {
		if( $format == 'Y-m-d')
			return $this->date;
		else
			return self::formatDate( $this->date, $format );
	}
	/**
	 * @return Int
	 */
	public function dateNum() {
		return (int)$this->dateNum;
	}
	/**
	 * @return String
	 */
	public function year() {
		return substr($this->dateNum,0,4);
	}
	/**
	 * @return String
	 */
	public function month() {
		return substr($this->dateNum,4,2);
	}
	/**
	 * @return String
	 */
	public function day() {
		return substr($this->dateNum,6,2);
	}
	/**
	 * Datetime timestamp
	 * @return Int
	 */
	public function stamp() {
	
		if( $this->date == 0 )
			return 0;
			
		$parse = self::parseDate( $this->date );
		return mktime( $parse['hour'], $parse['minute'], $parse['second'], $parse['month'], $parse['day'], $parse['year']);
	}
	
	/**
	 * @param String $var
	 * @return Mixed
	 */
	public function __get( $var ) {
		switch( $var ) {
			case 'dateNum':
				return $this->dateNum();
			break;
			case 'date':
				return $this->date();
			break;
			case 'year':
				return $this->year();
			break;
			case 'month':
				return $this->month();
			break;
			case 'day':
				return $this->day();
			break;
			case 'stamp':
				return $this->stamp();
			break;
			case 'isNull':
				return $this->isNull();
			break;
			case 'inPast':
				return $this->inPast();
			break;
			case 'inFuture':
				return $this->inFuture();
			break;
			default:
				throw new Exception('Can\'t access non existing property '.$var.'');
			break;
		}
	}
	
	// -----------------------------------------------------
	// SET
	// -----------------------------------------------------
	
	/**
	 * @param String $date
	 */
	public function setDate( $date ) {
		
		$this->changed();
		
		//set null
		if( $date == '' ) {
			 $this->date = NULL;
			 $this->dateNum = NULL;
			 return;
		
		// set now	
		} elseif( $date === 'now' )
			$date = date('Y-m-d');
		
		else if( strlen($date) == 4 && is_numeric($date) )
			$date = $date.'-00-00';
		
		// is timestamp
		else if( is_numeric($date) === true )
			$date = date('Y-m-d', $date );
		
		else if( $this->validate( $date ) === false ) {
			if( $this->getParent() ) {
				$this->getParent()->registerFailedValidation( 1, $this->getProeprty()->name, $date );
			} else {
				throw new InvalidArgumentException('Invalid date format <strong>'.$date.'</strong>');
			}
		}
		
		$parse = self::parseDate( $date );
		$this->date = $parse['year'].'-'.$parse['month'].'-'.$parse['day'];
		$this->dateNum = $parse['year'].$parse['month'].$parse['day'];
	}

	/**
	 * @param String $var
	 * @param Mixed $value
	 */
	public function __set( $var, $value ) {
		switch( $var ) {
			case 'date':
				$this->setDate($value);
			break;
			case 'stamp':
				$this->setDate( $value );
			break;
			default:
				throw new Exception('Can\'t access non existing property '.$var.'');
			break;
		}
	}
	
	// -----------------------------------------------------
	// HELPERS
	// -----------------------------------------------------

	/**
	* Converts date to format
	*
	* Allowed format letters
	* <ul>
	* <li> Y - year</li>
	* <li> m - month</li>
	* <li> d - day</li>
	* </ul>
	*
	* @param String $date Format Y-m-d
	* @param String $format to which date will be converted.
	*/
	public static function formatDate( $date, $format ) {
		if( $date == 0 )
			return '';
	
		$parse = self::parseDate( $date );
		$newDate = $format;
		
		$newDate = str_replace('Y',$parse['year'],$newDate);
		$newDate = str_replace('m',$parse['month'],$newDate);
		$newDate = str_replace('d',$parse['day'],$newDate);
		return $newDate;
	}
	/**
	* Date allowed format is (year)(string)(month)(string)(day)(string)(hour)(string)(minute)(string)(second)
	* @return Array with keys year, month, day, hour, minute, second
	*/
	public static function parseDate( $date ) {
		
		$data = array();
		$p = 0;
		$l = strlen($date);
		for( $i=0; $i<=$l;$i++) {
			if( is_numeric($date[$i]) === false or $i == $l ) {
				$data[] = substr($date, $p, $i-$p );
				$p = $i+1;
			}
		}
		
		$parse = array(
			'year' => (int)$data[0],
			'month' => (int)$data[1],
			'day' => (int)$data[2],
			'hour' => (int)$data[3],
			'minute' => (int)$data[4],
			'second' => (int)$data[5],
		);


		if( $parse['year'] < 10 ) 
			$parse['year'] = '000'.$parse['year']; 
		elseif( $parse['year'] < 100 ) 
			$parse['year'] = '00'.$parse['year']; 
		elseif( $parse['year'] < 1000 )
			$parse['year'] = '0'.$parse['year'];
		
		
		if( $parse['month'] < 10 ) 
			$parse['month'] = '0'.$parse['month'];
		
		
		if( $parse['day'] < 10 ) 
			$parse['day'] = '0'.$parse['day'];
		
		
		if( $parse['hour'] < 10 ) 
			$parse['hour'] = '0'.$parse['hour'];
		
		
		if( $parse['minute'] < 10 ) 
			$parse['minute'] = '0'.$parse['minute'];
		
		
		if( $parse['second'] < 10 ) 
			$parse['second'] = '0'.$parse['second'];
		
		return $parse;
	}
	/**
	 * @return String
	 */
	public function __toString() {
		return (string)$this->date();
	}
}



/**
* @package DomAr
* @subpackage Plugin
*/
class DomArProperty_DomArDate extends DomArProperty_Plugin implements IDomArPropertyAlterTableListener,IDomArPropertyCompareListener,IDomArPropertyInsertListener,IDomArPropertyUpdateListener,IDomArPropertyBeforeSetListener {
	
	protected $defaultValue;
	
	public function __construct( $name, $defaultValue = NULL ) {
		parent::__construct( $name );
		$this->defaultValue = $defaultValue;
	}
	
	public function init( DomArObject $object, array &$values ) {
		return (string)$values[$this->name];
	}
	
	public function load( DomArObject $object, &$propertyValue ) {
		
		$d = new DomArDate( ($propertyValue !== NULL ? $propertyValue : ($this->defaultValue ? $this->defaultValue : NULL)) );
		$d->changeParent( $object, $this );
		return $d;
	}
	
	public function compare( DomArObject $object, &$oldPropertyValue, &$newPropertyValue ) {
		$a  = is_object($newPropertyValue) ? $newPropertyValue : new DomArDate($newPropertyValue);
		return $oldPropertyValue->date() === $a->date();
	}
	
	public function beforeSet( DomArObject $object, &$oldPropertyValue, &$newPropertyValue ) {
		if( is_object($newPropertyValue) ) {
			$newPropertyValue->changeParent( $object, $this );
		} else {
			$date = $newPropertyValue;
			$newPropertyValue = new DomArDate( 'now');
			$newPropertyValue->changeParent( $object, $this );
			$newPropertyValue->setDate( $date );
		}
	}
	
	public function isLoaded( DomArObject $object, &$propertyValue ) {
		return is_object($propertyValue);
	}
	
	public function validate( DomArObject $object, &$propertyValue, array &$customParams = array() ) {
		return (is_object($propertyValue) && get_class($propertyValue) == 'DomArDate' or is_string($propertyValue) && DomArDate::validate($propertyValue) or is_numeric($propertyValue)) ? 0 : 1;
	}
	
	public function insert( DomArObject $object, &$propertyValue, DomArValueSet $set  ) {
		$propertyValue = $object->{$this->name};
		if( is_object($propertyValue) )
			$value = ($propertyValue->isNull() ? 'NULL' : '\''.$propertyValue->date('Y-m-d').'\'');
		else
			$value = ($propertyValue == '' ? 'NULL' : '\''.$propertyValue.'\'');
			
		$set->set( $this->name, $value );
	}
	
	public function update( DomArObject $object, &$propertyValue, DomArValueSet $set ) {
		$set->set( $this->name, ($propertyValue->isNull() ? 'NULL' : '\''.$propertyValue->date('Y-m-d').'\'') );
	}
	
	public function alterTable( DomArTable $table ) {
		return $table->changeColumn( $this->name, 'date', true );
	}
}
?>